﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Documents;
using System.Windows.Controls.Primitives;
using System.Windows;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Windows.Media;
using System.Reflection;

namespace Sinacor.Infra.UI.Controls.SmartClient
{
    public class SinacorNavigator : Selector
    {
        #region Controles
        SinacorInt32TextBox PART_TxtPosition = null;
        ToggleButton btnDisplayPopup = null;
        ListBox PART_ListBox = null;
        #endregion

        #region Constructors
        public SinacorNavigator()
        {
            CommandBindings.Add(new CommandBinding(SinacorNavigatorCommands.NavigateToFirst, NavigateToFirst, CanNavigateToFirst));
            CommandBindings.Add(new CommandBinding(SinacorNavigatorCommands.NavigateToLast, NavigateToLast, CanNavigateToLast));
            CommandBindings.Add(new CommandBinding(SinacorNavigatorCommands.NavigateToNext, NavigateToNext, CanNavigateToNext));
            CommandBindings.Add(new CommandBinding(SinacorNavigatorCommands.NavigateToPrevious, NavigateToPrevious, CanNavigateToPrevious));
            CommandBindings.Add(new CommandBinding(SinacorNavigatorCommands.DisplayPopup, DisplayPopup, CanDisplayPopup));

            this.SelectionChanged += new SelectionChangedEventHandler(SinacorNavigator_SelectionChanged);
            this.PreviewKeyDown += new KeyEventHandler(SinacorNavigator_PreviewKeyDown);
            this.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(SinacorNavigator_LostKeyboardFocus);
        }

        private void RaiseKeyEvent(Key key)
        {
            KeyEventArgs keyEvent = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key);
            keyEvent.RoutedEvent = Keyboard.PreviewKeyDownEvent;
            InputManager.Current.ProcessInput(keyEvent);
        }

        void SinacorNavigator_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                this.Close();
            }
            else if (e.Key == Key.Enter)
            {
                RaiseKeyEvent(Key.Space);
                e.Handled = true;
            }
            else if (e.Key == Key.Tab && btnDisplayPopup.IsChecked != null && btnDisplayPopup.IsChecked.Value)
            {
                if (this.SelectionMode == SelectionMode.Single)
                {
                    if (Keyboard.Modifiers == ModifierKeys.Shift)
                        NavigateToPrevious();
                    else
                        NavigateToNext();

                    e.Handled = true;
                }
                else
                {
                    if (SetFocusToListBox())
                        e.Handled = true;
                    else
                    {
                        if (Keyboard.Modifiers == ModifierKeys.Shift)
                            RaiseKeyEvent(Key.Up);
                        else
                            RaiseKeyEvent(Key.Down);

                        e.Handled = true;

                    }

                }

            }
            else if (e.Key == Key.Up && btnDisplayPopup.IsChecked != null && btnDisplayPopup.IsChecked.Value)
            {
                if (this.SelectionMode == SelectionMode.Single)
                {
                    NavigateToPrevious();
                    e.Handled = true;
                }
                else
                {
                    if (SetFocusToListBox())
                        e.Handled = true;
                }
            }
            else if (e.Key == Key.Down && btnDisplayPopup.IsChecked != null && btnDisplayPopup.IsChecked.Value)
            {
                if (this.SelectionMode == SelectionMode.Single)
                {
                    NavigateToNext();
                    e.Handled = true;

                }
                else
                {
                    if (SetFocusToListBox())
                        e.Handled = true;
                }
            }

        }

        private bool SetFocusToListBox()
        {
            bool focusSetted = false;

            if (!PART_ListBox.IsKeyboardFocusWithin)
            {
                if (this.HasItems)
                {
                    object firstItem = PART_ListBox.Items[0];
                    ListBoxItem listBoxItem = (ListBoxItem)PART_ListBox.ItemContainerGenerator.ContainerFromItem(firstItem);
                    listBoxItem.Focus();
                    focusSetted = true;
                }
            }

            return focusSetted;
        }

        private void ShowSelectedItem()
        {
            if (PART_ListBox != null )
            {
                if (PART_ListBox.SelectedIndex >= 0 && PART_ListBox.SelectedItem != null)
                    PART_ListBox.ScrollIntoView(PART_ListBox.SelectedItem);
            }
        }
        void SinacorNavigator_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (!this.IsKeyboardFocusWithin)
                Close();
        }

        static SinacorNavigator()
        {
            //SelectedIndexProperty.OverrideMetadata(typeof(SinacorNavigator), new FrameworkPropertyMetadata(SelectedIndexProperty.DefaultMetadata.PropertyChangedCallback, CoerceSelectedIndex));

        }
        #endregion

        private void PART_Listbox_OnApplyTemplate()
        {

        }
        void SinacorNavigator_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //se houver algum item selecionado
			if (this.SelectedIndex >= 0)
				this.Position = this.SelectedIndex + 1;
			////se n houver itens selecionado mais houver itens associados
			else if (this.Items.Count > 0)
				this.SelectedIndex = 0;
			else
				this.Position = 0;

        }

        #region Propriedades
        public static readonly DependencyProperty PositionProperty = DependencyProperty.Register("Position", typeof(int), typeof(SinacorNavigator), new FrameworkPropertyMetadata(0));
        public int Position
        {
            get { return (int)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }

        /// <summary>
        /// Indica a posição do controle na tela.
        /// </summary>
        public static readonly DependencyProperty NavigatorPlacementProperty = DependencyProperty.Register("NavigatorPlacement", typeof(PlacementType), typeof(SinacorNavigator), new FrameworkPropertyMetadata(PlacementType.Bottom));
        public PlacementType NavigatorPlacement
        {
            get { return (PlacementType)GetValue(NavigatorPlacementProperty); }
            set { SetValue(NavigatorPlacementProperty, value); }
        }

        public bool CanvasOnPopup
        {
            get { return (bool)GetValue(CanvasOnPopupProperty); }
            set { SetValue(CanvasOnPopupProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CanvasOnPopup.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CanvasOnPopupProperty =
            DependencyProperty.Register("CanvasOnPopup", typeof(bool), typeof(SinacorNavigator), new FrameworkPropertyMetadata(false));


        public SelectionMode SelectionMode
        {
            get { return (SelectionMode)GetValue(SelectionModeProperty); }
            set { SetValue(SelectionModeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectionMode.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectionModeProperty =
            DependencyProperty.Register("SelectionMode", typeof(SelectionMode), typeof(SinacorNavigator), new FrameworkPropertyMetadata(SelectionMode.Single));



        public AlignmentType AlignmentPopup
        {
            get { return (AlignmentType)GetValue(AlignmentPopupProperty); }
            set { SetValue(AlignmentPopupProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AlignmentPopup.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AlignmentPopupProperty = DependencyProperty.Register("AlignmentPopup", typeof(AlignmentType), typeof(SinacorNavigator), new FrameworkPropertyMetadata(AlignmentType.Left));


        /// <summary>
        /// Indicar a altura máxima que o popup de itens pode ter, tem por default o valor de 340 px
        /// </summary>
        public double MaxPopupHeight
        {
            get { return (double)GetValue(MaxPopupHeightProperty); }
            set { SetValue(MaxPopupHeightProperty, value); }
        }

        public static readonly DependencyProperty MaxPopupHeightProperty =
            DependencyProperty.Register("MaxPopupHeight", typeof(double), typeof(SinacorNavigator), new FrameworkPropertyMetadata((double)340));


        #endregion

        #region Métodos
        /// <summary>
        /// Verificar se pode navegar para o primeiro item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CanNavigateToFirst(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanNavigateToPrevious();
        }

        /// <summary>
        /// Verificar se pode navegar para item anterior
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CanNavigateToPrevious(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanNavigateToPrevious();
        }

        private bool CanNavigateToPrevious()
        {
            //return (this.HasItems && this.Items.CurrentPosition > 0);
            return (this.HasItems);
        }

        /// <summary>
        /// Verificar se pode navegar para o ultimo item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CanNavigateToLast(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanNavigateToNext();
        }

        /// <summary>
        /// Verificar se pode navegar para o proximo item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CanNavigateToNext(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CanNavigateToNext();
        }

        private bool CanNavigateToNext()
        {
            //return (this.HasItems && this.Items.CurrentPosition < this.Items.Count - 1);
            return (this.HasItems);
        }

        /// <summary>
        /// Verificar se pode apresentar a popup
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CanDisplayPopup(object sender, CanExecuteRoutedEventArgs e)
        {
            if (this.HasItems)
                e.CanExecute = true;
            else
                e.CanExecute = false;
        }


        protected void NavigateToFirst(object sender, EventArgs e)
        {
            if (this.HasItems)
            {
                this.Items.MoveCurrentToFirst();
                ShowSelectedItem();
            }
        }

        protected void NavigateToLast(object sender, EventArgs e)
        {
            if (this.HasItems)
            {
                this.Items.MoveCurrentToLast();
                ShowSelectedItem();
            }
        }

        protected void NavigateToNext(object sender, EventArgs e)
        {
            NavigateToNext();
        }

        protected void NavigateToPrevious(object sender, EventArgs e)
        {
            NavigateToPrevious();
        }

        protected void NavigateToNext()
        {
            if (CanNavigateToNext())
            {
                if (this.SelectedIndex == this.Items.Count - 1)
                    this.Items.MoveCurrentToFirst();
                else
                    this.Items.MoveCurrentToNext();

                ShowSelectedItem();
            }
        }

        protected void NavigateToPrevious()
        {
            if (CanNavigateToPrevious())
            {
                if (this.SelectedIndex == 0)
                    this.Items.MoveCurrentToLast();
                else
                    this.Items.MoveCurrentToPrevious();

                ShowSelectedItem();
            }
        }

        //protected void MoveFocusToNext()
        //{
        //    if (CanNavigateToNext())
        //    {
        //        int index = 0;
        //        PART_ListBox.Items.FindByValue(dr("UserId")).Selected = True

        //        if (this.HighlightedItemIndex != this.Items.Count - 1)
        //            index = this.HighlightedItemIndex + 1;

        //        ListBoxItem item = (ListBoxItem)PART_ListBox.Items[index];
        //        item.Focus();
        //    }
        //}

        //protected void MoveFocusToPrevious()
        //{
        //    if (CanNavigateToNext())
        //    {
        //        int index = this.Items.Count - 1;

        //        if (this.HighlightedItemIndex != 0)
        //            index = this.HighlightedItemIndex - 1;

        //        ListBoxItem item = (ListBoxItem)PART_ListBox.Items[index];
        //        item.Focus();
        //    }
        //}

        protected void DisplayPopup(object sender, EventArgs e)
        {
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new SinacorNavigatorItem();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is SinacorNavigatorItem);
        }

        private void SetItemSource()
        {
            int position = 0;

            if (!String.IsNullOrEmpty(PART_TxtPosition.Text))
            {
                if (Convert.ToInt32(PART_TxtPosition.Text) != 0)
                {
                    if (Convert.ToInt32(PART_TxtPosition.Text) > this.Items.Count - 1)
                        position = this.Items.Count - 1;
                    else
                        position = Convert.ToInt32(PART_TxtPosition.Text) - 1;
                }
            }
            else
                position = 0;

            this.SelectedIndex = position;
            PART_TxtPosition.Text = (position + 1).ToString();

        }


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            btnDisplayPopup = this.GetTemplateChild("btnDisplayPopup") as ToggleButton;
            PART_ListBox = this.GetTemplateChild("PART_ListBox") as ListBox;
            PART_ListBox.SelectionChanged += new SelectionChangedEventHandler(PART_ListBox_SelectionChanged);

            PART_TxtPosition = this.GetTemplateChild("PART_TxtPosition") as SinacorInt32TextBox;
            PART_TxtPosition.LostFocus += new RoutedEventHandler(PART_TxtPosition_LostFocus);
            PART_TxtPosition.KeyDown += new KeyEventHandler(PART_TxtPosition_KeyDown);

        }

        void PART_ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Quando o tipo de seleção for Single, o Highlight do ListBox deve acompanhar
            if (this.SelectionMode == SelectionMode.Single && this.HasItems && PART_ListBox.SelectedIndex != -1)
            {
                object item = PART_ListBox.Items[PART_ListBox.SelectedIndex];
                ListBoxItem listBoxItem = (ListBoxItem)PART_ListBox.ItemContainerGenerator.ContainerFromItem(item);

                if(listBoxItem != null)
                    listBoxItem.Focus();
            }

        }

        void PART_TxtPosition_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                SetItemSource();
            }
        }

        void PART_TxtPosition_LostFocus(object sender, RoutedEventArgs e)
        {
            SetItemSource();
        }

        /// <summary>
        /// Permite abrir a lista de itens programaticamente
        /// </summary>
        public void Open()
        {
            if (btnDisplayPopup != null)
            {
                btnDisplayPopup.IsChecked = true;
                this.Focus();
            }
        }

        /// <summary>
        /// Permite fechar a lista de itens programaticamente
        /// </summary>
        public void Close()
        {
            if (btnDisplayPopup != null)
            {
                btnDisplayPopup.IsChecked = false;
            }
        }
        #endregion




        public double ActualWidthPopup
        {
            get { return (double)GetValue(ActualWidthPopupProperty); }
            set { SetValue(ActualWidthPopupProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ActualWidthPopup.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ActualWidthPopupProperty =
            DependencyProperty.Register("ActualWidthPopup", typeof(double), typeof(SinacorNavigator), new UIPropertyMetadata((double)0));



        public double WidthPopup
        {
            get { return (double)GetValue(WidthPopupProperty); }
            set { SetValue(WidthPopupProperty, value); }
        }

        // Using a DependencyProperty as the backing store for WidthPopup.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WidthPopupProperty =
            DependencyProperty.Register("WidthPopup", typeof(double), typeof(SinacorNavigator), new FrameworkPropertyMetadata((double)0, OnWidthPopupChanged));


        private static void OnWidthPopupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(d))
                return;

            SinacorNavigator instance = d as SinacorNavigator;

            if (instance != null)
            {
                instance.ActualWidthPopup = instance.WidthPopup;
            }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            if (e.Property == WidthProperty || e.Property == ActualWidthProperty)
            {
                if (this.WidthPopup == 0)
                {
                    double newWidth = 0;
                    newWidth = (double)e.NewValue;
                    this.ActualWidthPopup = newWidth;
                }
                else
                    this.ActualWidthPopup = this.WidthPopup;

                if (this.ActualWidthPopup > this.Width + 8)
                    this.CanvasOnPopup = true;
                else
                    this.CanvasOnPopup = false;
            }
        }
    }


    public class SinacorNavigatorItem : ListBoxItem
    {
    }
}